#include "board_spi.h"

#if BOARD_SPIn > 0
SPI_HandleTypeDef spi1handler;
#endif

GPIO_TypeDef* SPI_DEV_PORT[BOARD_SPI_DEVn] = {
#if BOARD_SPI_DEVn > 0
	BOARD_SPI_DEV1_CS_GPIO_PORT
#endif
#if BOARD_SPI_DEVn > 1
	, BOARD_SPI_DEV2_CS_GPIO_PORT
#endif
#if BOARD_SPI_DEVn > 2
	, BOARD_SPI_DEV3_CS_GPIO_PORT
#endif
};

const uint16_t SPI_DEV_CS_PIN[BOARD_SPI_DEVn] = {
#if BOARD_SPI_DEVn > 0
	BOARD_SPI_DEV1_CS_PIN
#endif
#if BOARD_SPI_DEVn > 1
	, BOARD_SPI_DEV2_CS_PIN
#endif
#if BOARD_SPI_DEVn > 2
	, BOARD_SPI_DEV3_CS_PIN
#endif
};

void board_spi_init(SPI_HandleTypeDef *spihandler)
{
#if BOARD_SPIn > 0
	if (spihandler == &spi1handler) {
		spihandler->Instance = BOARD_SPI1;
		spihandler->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
		spihandler->Init.Direction = SPI_DIRECTION_2LINES;
		spihandler->Init.CLKPhase = SPI_PHASE_2EDGE;
		spihandler->Init.CLKPolarity = SPI_POLARITY_HIGH;
		spihandler->Init.CRCCalculation	= SPI_CRCCALCULATION_DISABLE;
		spihandler->Init.CRCPolynomial = 7;
		spihandler->Init.DataSize = SPI_DATASIZE_8BIT;
		spihandler->Init.FirstBit = SPI_FIRSTBIT_MSB;
		spihandler->Init.NSS = SPI_NSS_SOFT;
		spihandler->Init.TIMode	= SPI_TIMODE_DISABLE;
		spihandler->Init.Mode = SPI_MODE_MASTER;
	}
	HAL_SPI_Init(spihandler);
#endif
}

/**
 * @brief SPI MSP Initialization 
 *        This function configures the hardware resources used in this example: 
 *           - Peripheral's clock enable
 *           - Peripheral's GPIO Configuration  
 *           - DMA configuration for transmission request by peripheral 
 *           - NVIC configuration for DMA interrupt request enable
 * @param hspi: SPI handle pointer
 * @retval None
 */
void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
{
#if BOARD_SPIn > 0
	static DMA_HandleTypeDef hdma1_tx;
	static DMA_HandleTypeDef hdma1_rx;
#endif
	GPIO_InitTypeDef  GPIO_InitStruct;
#if BOARD_SPI_DEVn > 0
	/* Configure peripheral GPIO */
	/* Enable devices' cs pin clock */
	BOARD_SPI_DEV1_CS_GPIO_CLK_ENABLE();
	/* devices' cs pin configuration */
	GPIO_InitStruct.Pin = BOARD_SPI_DEV1_CS_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	HAL_GPIO_Init(BOARD_SPI_DEV1_CS_GPIO_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(BOARD_SPI_DEV1_CS_GPIO_PORT,
			  BOARD_SPI_DEV1_CS_PIN,
			  GPIO_PIN_SET);
#endif
#if BOARD_SPI_DEVn > 1
	/* Configure peripheral GPIO */
	/* Enable devices' cs pin clock */
	BOARD_SPI_DEV2_CS_GPIO_CLK_ENABLE();
	/* devices' cs pin configuration */
	GPIO_InitStruct.Pin = BOARD_SPI_DEV2_CS_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	HAL_GPIO_Init(BOARD_SPI_DEV2_CS_GPIO_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(BOARD_SPI_DEV2_CS_GPIO_PORT,
			  BOARD_SPI_DEV2_CS_PIN,
			  GPIO_PIN_SET);
#endif
#if BOARD_SPI_DEVn > 2
	/* Configure peripheral GPIO */
	/* Enable devices' cs pin clock */
	BOARD_SPI_DEV3_CS_GPIO_CLK_ENABLE();
	/* devices' cs pin configuration */
	GPIO_InitStruct.Pin = BOARD_SPI_DEV3_CS_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	HAL_GPIO_Init(BOARD_SPI_DEV3_CS_GPIO_PORT, &GPIO_InitStruct);
	HAL_GPIO_WritePin(BOARD_SPI_DEV3_CS_GPIO_PORT,
			  BOARD_SPI_DEV3_CS_PIN,
			  GPIO_PIN_SET);
#endif

#if BOARD_SPIn > 0
	if (hspi == &spi1handler) {
		/*##-1- Enable peripherals and GPIO Clocks */
		/* Enable GPIO TX/RX clock */
		BOARD_SPI1_SCK_GPIO_CLK_ENABLE();
		BOARD_SPI1_MISO_GPIO_CLK_ENABLE();
		BOARD_SPI1_MOSI_GPIO_CLK_ENABLE();
		/* Enable SPI clock */
		BOARD_SPI1_CLK_ENABLE();
		/* Enable DMA1 clock */
		BOARD_SPI1_DMA_CLK_ENABLE();

		/* SPI SCK GPIO pin configuration  */
		GPIO_InitStruct.Pin       = BOARD_SPI1_SCK_PIN;
		GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
		GPIO_InitStruct.Pull      = GPIO_PULLUP;
		GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
		GPIO_InitStruct.Alternate = BOARD_SPI1_AF;
		HAL_GPIO_Init(BOARD_SPI1_SCK_GPIO_PORT, &GPIO_InitStruct);

		/* SPI MISO GPIO pin configuration  */
		GPIO_InitStruct.Pin = BOARD_SPI1_MISO_PIN;
		HAL_GPIO_Init(BOARD_SPI1_MISO_GPIO_PORT, &GPIO_InitStruct);

		/* SPI MOSI GPIO pin configuration  */
		GPIO_InitStruct.Pin = BOARD_SPI1_MOSI_PIN;
		HAL_GPIO_Init(BOARD_SPI1_MOSI_GPIO_PORT, &GPIO_InitStruct);

		/*##-3- Configure the DMA streams */
		/* Configure the DMA handler for Transmission process */
		hdma1_tx.Instance                 = BOARD_SPI1_TX_DMA_STREAM;

		hdma1_tx.Init.Channel             = BOARD_SPI1_TX_DMA_CHANNEL;
		hdma1_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
		hdma1_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
		hdma1_tx.Init.MemInc              = DMA_MINC_ENABLE;
		hdma1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma1_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
		hdma1_tx.Init.Mode                = DMA_NORMAL;
		hdma1_tx.Init.Priority            = DMA_PRIORITY_LOW;
		hdma1_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
		hdma1_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
		hdma1_tx.Init.MemBurst            = DMA_MBURST_INC4;
		hdma1_tx.Init.PeriphBurst         = DMA_PBURST_INC4;

		HAL_DMA_Init(&hdma1_tx);

		/* Associate the initialized DMA handle to the the SPI handle */
		__HAL_LINKDMA(hspi, hdmatx, hdma1_tx);

		/* Configure the DMA handler for Transmission process */
		hdma1_rx.Instance                 = BOARD_SPI1_RX_DMA_STREAM;

		hdma1_rx.Init.Channel             = BOARD_SPI1_RX_DMA_CHANNEL;
		hdma1_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
		hdma1_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
		hdma1_rx.Init.MemInc              = DMA_MINC_ENABLE;
		hdma1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
		hdma1_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
		hdma1_rx.Init.Mode                = DMA_NORMAL;
		hdma1_rx.Init.Priority            = DMA_PRIORITY_HIGH;
		hdma1_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
		hdma1_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
		hdma1_rx.Init.MemBurst            = DMA_MBURST_INC4;
		hdma1_rx.Init.PeriphBurst         = DMA_PBURST_INC4;

		HAL_DMA_Init(&hdma1_rx);

		/* Associate the initialized DMA handle to the the SPI handle */
		__HAL_LINKDMA(hspi, hdmarx, hdma1_rx);

		/*##-4- Configure the NVIC for DMA */
		/* NVIC configuration for DMA transfer complete interrupt (SPI1_TX) */
		HAL_NVIC_SetPriority(BOARD_SPI1_DMA_TX_IRQn, 6, 0);
		HAL_NVIC_EnableIRQ(BOARD_SPI1_DMA_TX_IRQn);

		/* NVIC configuration for DMA transfer complete interrupt (SPI3_RX) */
		HAL_NVIC_SetPriority(BOARD_SPI1_DMA_RX_IRQn, 0, 0);
		HAL_NVIC_EnableIRQ(BOARD_SPI1_DMA_RX_IRQn);
	}
#endif
}

/**
 * @brief SPI MSP De-Initialization 
 *        This function frees the hardware resources used in this example:
 *          - Disable the Peripheral's clock
 *          - Revert GPIO, DMA and NVIC configuration to their default state
 * @param hspi: SPI handle pointer
 * @retval None
 */
void HAL_SPI_MspDeInit(SPI_HandleTypeDef *hspi)
{
#if BOARD_SPIn > 0
	static DMA_HandleTypeDef hdma1_tx;
	static DMA_HandleTypeDef hdma1_rx;
#endif

#if BOARD_SPIn > 0
	__HAL_LINKDMA(hspi, hdmatx, hdma1_tx);
	__HAL_LINKDMA(hspi, hdmarx, hdma1_rx);
#endif

#if BOARD_SPIn > 0
	if (hspi == &spi1handler) {
		/*##-1- Reset peripherals */
		BOARD_SPI1_FORCE_RESET();
		BOARD_SPI1_RELEASE_RESET();

		/*##-2- Disable peripherals and GPIO Clocks */
		/* Configure SPI SCK as alternate function  */
		HAL_GPIO_DeInit(BOARD_SPI1_SCK_GPIO_PORT, BOARD_SPI1_SCK_PIN);
		/* Configure SPI MISO as alternate function  */
		HAL_GPIO_DeInit(BOARD_SPI1_MISO_GPIO_PORT, BOARD_SPI1_MISO_PIN);
		/* Configure SPI MOSI as alternate function  */
		HAL_GPIO_DeInit(BOARD_SPI1_MOSI_GPIO_PORT, BOARD_SPI1_MOSI_PIN);

		/*##-3- Disable the DMA Streams */
		/* De-Initialize the DMA Stream associate to transmission process */
		HAL_DMA_DeInit(&hdma1_tx);
		/* De-Initialize the DMA Stream associate to reception process */
		HAL_DMA_DeInit(&hdma1_rx);

		/*##-4- Disable the NVIC for DMA */
		HAL_NVIC_DisableIRQ(BOARD_SPI1_DMA_TX_IRQn);
		HAL_NVIC_DisableIRQ(BOARD_SPI1_DMA_RX_IRQn);
	}
#endif
}

void spi_select(uint8_t dev, uint8_t state)
{
#if BOARD_SPI_DEVn > 0
	if (dev > BOARD_SPI_DEVn || dev == 0)
		return;
	if (state == 0) {
		HAL_GPIO_WritePin(SPI_DEV_PORT[dev - 1],
				  SPI_DEV_CS_PIN[dev - 1],
				  GPIO_PIN_SET);
		return;
	}
	HAL_GPIO_WritePin(SPI_DEV_PORT[dev - 1],
			  SPI_DEV_CS_PIN[dev - 1],
			  GPIO_PIN_RESET);
#endif
}

